/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.ipti.br.tag.bo;

import org.ipti.br.tag.dao.ActivityFacade;
import org.ipti.br.tag.dao.WorkFacade;
import org.ipti.br.tag.dao.WorkTopicFacade;
import org.ipti.br.tag.entity.Discipline;
import org.ipti.br.tag.entity.Organization;
import org.ipti.br.tag.entity.Work;
import org.ipti.br.tag.entity.WorkTopic;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.logging.Level;
import javax.ejb.Stateless;
import javax.inject.Inject;
import org.ipti.br.tag.util.LoggerUtil;

/**
 *
 * @author ipti
 */
@Stateless
public class WorkBO {
    
    @Inject
    private WorkFacade workFacade;
    
    @Inject
    private WorkTopicFacade topicFacade;
    
    @Inject
    private ActivityFacade activityFacade;
    
    public List<Work> listWorks(int year, Organization org, Discipline discipline){
        Calendar c = Calendar.getInstance();
        c.set(Calendar.DAY_OF_MONTH,15);
        c.set(Calendar.MONTH,5);
        c.set(Calendar.YEAR,year);
        return workFacade.list(c, discipline, org);
    }
    
    public void createTopic(WorkTopic topic, int year) {
        Calendar c = Calendar.getInstance();
        c.set(Calendar.DAY_OF_MONTH,1);
        c.set(Calendar.MONTH,0);
        c.set(Calendar.YEAR,year);
        topic.setInitialDate(c);
        topic.setFinalDate(null);
        topic.setFather((topic.getFather() != null) ? getPrimary(topic.getFather()) : null);
        topic.setTop(topic.getFather());

        try {
            if(topic.getFather() != null){
                topic.getFather().getSons().add(topic);
            }
            topicFacade.create(topic);
        } catch(Exception e) {
            LoggerUtil.log("createTopic", Level.SEVERE,
                    "Não foi possível criar o tópico: " + topic.getName(), e);
        }
    }
    
    public void createWork(WorkTopic topic, Organization org, Discipline discipline, int year) {
        topic.setFather(null);
        createTopic(topic, year);
        try {
            getWorkFacade().create(new Work(topic, discipline, org));
        } catch(Exception e) {
            LoggerUtil.log("createWork", Level.SEVERE,
                    "Não foi possível criar o work: " + topic.getName(), e);
        }
    }
    
    public void renameTopic(WorkTopic topic, int year){
        if(year == topic.getInitialDate().get(Calendar.YEAR)) {
            this.correctSpelling(topic);
        } else {
            Calendar c = Calendar.getInstance(),d = Calendar.getInstance();
            c.set(Calendar.DAY_OF_MONTH,31);
            c.set(Calendar.MONTH,11);
            c.set(Calendar.YEAR,year);
            
            d.set(Calendar.DAY_OF_MONTH,1);
            d.set(Calendar.MONTH,0);
            d.set(Calendar.YEAR,year+1);
            
            WorkTopic replace = new WorkTopic();
            replace.setName(topic.getName());
            replace.setInitialDate(d);
            
            topic = topicFacade.find(topic.getId());
            
            topic.setFinalDate(c);
            topic.setReplace(replace);
            replace.setReplaced(topic);

            try{
                topicFacade.create(replace);
                topicFacade.edit(topic);
            }catch(Exception e){
                LoggerUtil.log("renameTopic", Level.SEVERE,
                        "Não foi possível renomear o tópico: " + topic.getId(), e);
            }
        }
    }
    
    public void disableTopic(WorkTopic topic, int year) { //pensar na árvore ao deletar
        Calendar c = Calendar.getInstance();
        c.set(Calendar.DAY_OF_MONTH,31);
        c.set(Calendar.MONTH,11);
        c.set(Calendar.YEAR,year);
        topic.setFinalDate(c);
        
        try{
           topicFacade.edit(topic);
        } catch(Exception e) {
            LoggerUtil.log("disableTopic", Level.SEVERE,
                    "Não foi possível desabilitar o tópico: " + topic.getId(), e);
        }
    }
    
    public void deleteTopic(WorkTopic topic) {
        try {
            Work w = workFacade.getRoot(topic);
            if (w != null) {
                workFacade.remove2(w);
            }
            workFacade.flush();
            WorkTopic father = topic.getFather();
            if (father != null) {
                father.getSons().remove(topic);
                topicFacade.remove(topic);
                topicFacade.edit(father);
            } else {
                topicFacade.remove(topic);
            }
//            workFacade.flush();
//            WorkTopic father = topic.getFather();
//            if (father == null) {
//                Work w = workFacade.getRoot(topic);
//                if (w != null) {
//                    workFacade.remove2(w);
//                }
//                topicFacade.remove(topic);
//            } else {
//                father.getSons().remove(topic);
//                topicFacade.remove(topic);
//                topicFacade.refresh(father);
//            }
        } catch(Exception e) {
            LoggerUtil.log("renameTopic", Level.SEVERE,
                    "Não foi possível deletar o tópico: " + topic.getId(), e);
        }
    }
    
    public WorkTopic findCode(Organization org, int code) {
        return topicFacade.getByCode(org, code);
    }
    
    public boolean canRemove(WorkTopic topic) {
        if (topic.getInitialDate().get(Calendar.YEAR) != Calendar.getInstance().get(Calendar.YEAR)) {
            return false;
        } else if (!topic.getSons().isEmpty()) {
            System.out.println("Sons: "+topic.getSons().size());
            return false;
        } else if (!activityFacade.list(topic).isEmpty()) {
            return false;
        }
        return true;
    }
    
    private void correctSpelling(WorkTopic topic) {
        try {
            topicFacade.edit(topic);
        } catch(Exception e) {
            LoggerUtil.log("correctSpelling", Level.SEVERE,
                    "Não foi possível corrigir o tópico: " + topic.getId(), e);
        }
    }
    
    
    public WorkTopic getPrimary(WorkTopic topic) {
        while(topic.getReplaced() != null) {
            topic = topic.getReplaced();
        }
               
        return topic;
    }
    
    public WorkTopic validateTopic(WorkTopic topic, int year) {
        if(topic.getInitialDate().get(Calendar.YEAR) <= year &&
                (topic.getFinalDate() == null || topic.getFinalDate().get(Calendar.YEAR) >= year)) {
            return topic;
        } else if(topic.getInitialDate().get(Calendar.YEAR) > year ) {
            return null;
        } else if(topic.getFinalDate() != null && topic.getFinalDate().get(Calendar.YEAR) < year ) {
            if(topic.getReplace() != null) {
                return validateTopic(topic.getReplace(), year);
            }
        }
        return null;
    }
    
    public List<WorkTopic> joinSons(WorkTopic topic) {
        if(topic.getReplace() == null) {
            return topic.getSons();
        } else {
            List<WorkTopic> temp = topic.getSons().subList(0, topic.getSons().size() - 1);
            temp.addAll(joinSons(topic.getReplace()));
            return temp;
        }
    }
    
    public void generateCodes(Organization org) {
//		buscar WTs não folha que possuem código por organização
//			remover códigos desses WTs
//		buscar todos os WT folhas que não possuem código e por organização, ordenados por disciplina e pai
//		buscar valor máximo de código utilizado na organização
//		varrer folhas gerando código incremental a partir de máximo utilizado
        List<WorkTopic> notLeafs = new ArrayList<WorkTopic>(), leafs = new ArrayList<WorkTopic>();
        Integer maxCode = 0;
        for (WorkTopic wt : workFacade.list(org)) {
            maxCode = Math.max(maxCode, temporary(wt, leafs, notLeafs));
        }
        try {
            for (WorkTopic workTopic : notLeafs) {
                workTopic.setCode(null);
                topicFacade.edit(workTopic);
            }
            topicFacade.flush();
            for (int i = 0; i < leafs.size(); i++) {
                leafs.get(i).setCode(i+1);
                topicFacade.edit(leafs.get(i));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    private Integer temporary(WorkTopic topic, List<WorkTopic> leafs, List<WorkTopic> notLeafs) {
        Integer max = 0;
        Queue<WorkTopic> queue = new LinkedList<WorkTopic>();
        queue.offer(topic);
        while (!queue.isEmpty()) {
            topic = queue.poll();
            if (topic.getCode() != null) {
                max = Math.max(max,topic.getCode());
            }
            if (topic.getSons().isEmpty()) {
                if (topic.getCode() == null || topic.getCode() == 0) {
                    leafs.add(topic);
                }
            } else if(topic.getCode() != null){
                notLeafs.add(topic);
            }
            for (WorkTopic workTopic : topic.getSons()) {
                queue.offer(workTopic);
            }
        }
        
        return max;
    }
    
    private Integer generateSequence(List<WorkTopic> topics) {
        int max = -1;
        for(WorkTopic wt : topics) {
            if(wt.getCode() > max) {
                max = wt.getCode();
            }
        }
        return max+1;
    }
    
    public List<WorkTopic> modifiedTopics(Calendar initialDate,Calendar finalDate,Long serialNumber) {
        return topicFacade.modifiedTopics(initialDate, finalDate, serialNumber);
    }
    
    public List<Work> modifiedWorks(Calendar initialDate,Calendar finalDate,Long serialNumber) {
        return workFacade.modifiedWorks(initialDate, finalDate, serialNumber);
    }

    public WorkTopicFacade getTopicFacade() {
        return topicFacade;
    }

    public void setTopicFacade(WorkTopicFacade topicFacade) {
        this.topicFacade = topicFacade;
    }

    public WorkFacade getWorkFacade() {
        return workFacade;
    }

    public void setWorkFacade(WorkFacade workFacade) {
        this.workFacade = workFacade;
    }
}
